#include "MyTcpSocket.h"
#include "MyTcpServer.h"

MyTcpSocket::MyTcpSocket() {
	m_bUpload = false;
	m_iTotal = 0;
	m_iReceived = 0;
	//m_pTimer = new QTimer;
	connect(this, SIGNAL(readyRead())
		, this, SLOT(recvMsg()));
	connect(this, SIGNAL(disconnected())
		, this, SLOT(clientOffline()));
	//connect(m_pTimer, SIGNAL(timeout())
	//	, this, SLOT(sendFileToClient()));
}

QString MyTcpSocket::getName()
{
	return m_strName;
}

QString MyTcpSocket::getPwd()
{
	return m_strPwd;
}

//接收到disconnect()信号(即：client断开)后执行
void MyTcpSocket::clientOffline()
{
	OpeDB::getInstance().handleOffline(m_strName.toStdString().c_str(),
		m_strPwd.toStdString().c_str());
	emit offline(this);
}

void MyTcpSocket::sendFileToClient()
{
	char* pData = new char[4096];
	qint64 ret = 0;
	while (1) {
		ret = m_file.read(pData, 4096);
		if (ret > 0 && ret <= 4096) {
			write(pData, ret);
		}
		else if (ret == 0) {
			m_file.close();
			break;
		}
		else if (ret < 0) {
			qDebug() << "发送文件给客户端过程中出错";
			m_file.close();
			break;
		}
	}
	delete[]pData;
	pData = NULL;
}

//接收到readyRead()信号后执行
void MyTcpSocket::recvMsg() {
	if (m_bUpload) {	//文件传输状态
		PDU* resPdu = NULL;
		QByteArray buff = readAll();
		if (m_file.exists()) {	//目标文件正常打开
			m_file.write(buff);
			m_iReceived += buff.size();
			if (m_iTotal == m_iReceived) {	//文件传输完成
				m_file.close();
				m_bUpload = false;
				resPdu = mkPDU(0);
				resPdu->uiMsgType = ENUM_MESSAGE_TYPE_UPLOAD_FILE_RESPOND;
				//产生回复PDU，提示上传成功
				strcpy(resPdu->caData, UPLOAD_OK);
				write((char*)(resPdu), resPdu->uiPDULen);
				free(resPdu);
				resPdu = NULL;
			}
			else if (m_iTotal < m_iReceived) {
				m_file.close();
				m_bUpload = false;
				resPdu = mkPDU(0);
				resPdu->uiMsgType = ENUM_MESSAGE_TYPE_UPLOAD_FILE_RESPOND;
				//产生回复PDU，提示上传成功
				strcpy(resPdu->caData, UPLOAD_FAILED);
				write((char*)(resPdu), resPdu->uiPDULen);
				free(resPdu);
				resPdu = NULL;
			}
		}
		else {	//目标文件丢失
			m_bUpload = false;
			resPdu = mkPDU(0);
			resPdu->uiMsgType = ENUM_MESSAGE_TYPE_UPLOAD_FILE_RESPOND;
			strcpy(resPdu->caData, UPLOAD_FAILED_FILE_NO_EXIST);
			free(resPdu);
			resPdu = NULL;
		}
	}
	else {	//非文件传输状态
		qDebug() << "当前服务器端socket中可读数据：" << this->bytesAvailable();
		uint uiPDULen = 0;
		//读取PDU中首字符，即PDU总长度
		this->read((char*)&uiPDULen, sizeof(uint));
		uint uiMsgLen = uiPDULen - sizeof(PDU);
		PDU* pdu = mkPDU(uiMsgLen);
		//read()将socket中内容按串写入PDU实例，首部已读出
		this->read((char*)pdu + sizeof(uint), uiPDULen - sizeof(uint));
		//根据数据PDU数据类型来处理PDU数据
		switch (pdu->uiMsgType) {
		case ENUM_MESSAGE_TYPE_REGIST_REQUEST: {
			char caName[32] = { '\0' };
			char caPwd[32] = { '\0' };
			strncpy(caName, pdu->caData, 32);
			strncpy(caPwd, pdu->caData + 32, 32);
			qDebug() << caName << caPwd << pdu->uiMsgType;
			bool ret = OpeDB::getInstance().handleRegist((char*)caName, (char*)caPwd);
			PDU* resPdu = mkPDU(0);
			resPdu->uiMsgType = ENUM_MESSAGE_TYPE_REGIST_RESPOND;
			if (ret) {
				strcpy(resPdu->caData, REGIST_OK);
				//注册成功后创建对应文件夹
				QDir dir;
				qDebug() << "已创建新的用户文件夹:" << caName <<
					dir.mkdir(QString("./%1").arg(caName));
			}
			else {
				strcpy(resPdu->caData, REGIST_FAILED);
			}
			this->write((char*)resPdu, resPdu->uiPDULen);
			//this->flush();
			free(resPdu);
			resPdu = NULL;
			break;
		}
		case ENUM_MESSAGE_TYPE_LOGIN_REQUEST: {
			char caName[32] = { '\0' };
			char caPwd[32] = { '\0' };
			strncpy(caName, pdu->caData, 32);
			strncpy(caPwd, pdu->caData + 32, 32);
			qDebug() << caName << caPwd << pdu->uiMsgType;
			bool ret = OpeDB::getInstance().handleLogin((char*)caName, (char*)caPwd);
			PDU* resPdu = mkPDU(0);
			resPdu->uiMsgType = ENUM_MESSAGE_TYPE_LOGIN_RESPOND;
			if (ret) {
				strcpy(resPdu->caData, LOGIN_OK);
				m_strName = caName;
				m_strPwd = caPwd;
			}
			else {
				strcpy(resPdu->caData, LOGIN_FAILED);
			}
			write((char*)resPdu, resPdu->uiPDULen);
			this->flush();
			free(resPdu);
			resPdu = NULL;
			break;
		}
		case ENUM_MESSAGE_TYPE_CREATE_DIR_REQUEST: {
			QDir dir;
			qDebug() << "当前dir的路径：" << dir.absolutePath();
			QString strCurPath = QString("%1").arg((char*)(pdu->caMsg));
			bool ret = dir.exists();
			//产生回复的pdu
			PDU* resPdu = mkPDU(0);
			resPdu->uiMsgType = ENUM_MESSAGE_TYPE_CREATE_DIR_RESPOND;
			//当前目录存在
			if (ret) {
				char caNewDir[32] = { '\0' };
				memcpy(caNewDir, pdu->caData + 32, 32);
				QString strNewPath = strCurPath + "/" + caNewDir;
				qDebug() << strNewPath;
				ret = dir.exists(strNewPath);
				qDebug() << "要创建的文件夹是否存在" << ret;
				//要创建的文件夹已存在
				if (ret) {
					strcpy(resPdu->caData, FILE_NAME_EXIST);
				}
				//要创建的文件夹不存在
				else {
					//执行创建文件夹
					dir.mkdir(strNewPath);
					strcpy(resPdu->caData, CREATE_DIR_OK);
				}
			}
			//当前目录不存在
			else {
				qDebug() << "当前目录不存在";
				strcpy(resPdu->caData, DIR_NO_EXIST);
			}
			write((char*)resPdu, resPdu->uiPDULen);
			free(resPdu);
			resPdu = NULL;
			break;
		}
		case ENUM_MESSAGE_TYPE_FLUSH_FILE_REQUEST: {
			qDebug() << "当前用户：" << m_strName << " " << m_strPwd;
			//取出当前目录位置
			char* pCurPath = new char[pdu->uiMsgLen];
			memcpy(pCurPath, pdu->caMsg, pdu->uiMsgLen);
			//获得目录下的文件信息 entryInfoList()
			//指定操作路径
			QDir dir(pCurPath);
			PDU* resPdu = NULL;
			if (!dir.exists()) {	//当前目录不存在，报错
				resPdu = mkPDU(0);
				pdu->uiMsgType = ENUM_MESSAGE_TYPE_FLUSH_FILE_RESPOND_FAILED;
				strcpy(pdu->caData, FLUSH_FILE_FAILED);
			}
			else {
				QFileInfoList fileInfoList = dir.entryInfoList();
				//去除.与..
				int iFileCount = fileInfoList.size();
				resPdu = mkPDU(sizeof(FileInfo) * (iFileCount - 2));
				resPdu->uiMsgType = ENUM_MESSAGE_TYPE_FLUSH_FILE_RESPOND;
				FileInfo* pFileInfo;
				QString strFileName;
				QByteArray temp;
				for (int i = 2; i < fileInfoList.size(); i++) {
					//方便使caMsg一次往后跳一个fileInfo
					pFileInfo = (FileInfo*)(resPdu->caMsg) + i - 2;
					strFileName = fileInfoList[i].fileName();
					temp = strFileName.toLocal8Bit();
					memcpy(pFileInfo->caFileName,
						temp.data(), temp.size());
					if (fileInfoList[i].isDir()) {
						pFileInfo->iFileType = 0;
					}
					else if (fileInfoList[i].isFile()) {
						pFileInfo->iFileType = 1;
					}
					qDebug() << fileInfoList[i].fileName()
						<< fileInfoList[i].size();
				}
			}
			write((char*)resPdu, resPdu->uiPDULen);
			free(resPdu);
			resPdu = NULL;
			break;
		}
		case ENUM_MESSAGE_TYPE_DELETE_DIR_REQUEST: {
			QString strDelPath = QString("%1/%2").arg((char*)(pdu->caMsg))
				.arg((char*)(pdu->caData));
			qDebug() << "当前删除的路径：" << strDelPath;
			QFileInfo fileInfo(strDelPath);
			bool ret = false;
			if (fileInfo.isDir()) {
				QDir dir;
				dir.setPath(strDelPath);
				ret = dir.removeRecursively();
			}
			else if (fileInfo.isFile()) {
				ret = false;
			}
			PDU* resPdu = mkPDU(0);
			resPdu->uiMsgType = ENUM_MESSAGE_TYPE_DELETE_DIR_RESPOND;
			if (ret) {
				//删除成功
				strcpy(resPdu->caData, DEL_DIR_OK);
			}
			else {
				strcpy(resPdu->caData, DEL_DIR_FAILED);
			}
			write((char*)resPdu, resPdu->uiPDULen);
			free(resPdu);
			resPdu = NULL;
			break;
		}
		case ENUM_MESSAGE_TYPE_RENAME_FILE_REQUEST: {
			QString strOldPath = QString("%1/%2").arg((char*)(pdu->caMsg))
				.arg((char*)(pdu->caData));
			QString strNewFileName = (char*)(pdu->caData + 32);
			QString strNewPath = QString("%1/%2").arg((char*)(pdu->caMsg))
				.arg(strNewFileName);
			QFile qFile;
			qFile.setFileName(strOldPath);
			bool ret = qFile.rename(strNewPath);
			PDU* resPdu = mkPDU(0);
			resPdu->uiMsgType = ENUM_MESSAGE_TYPE_RENAME_FILE_RESPOND;
			if (ret) {	//重命名成功
				strcpy(resPdu->caData, RENAME_OK);
			}
			else {		//重命名失败
				strcpy(resPdu->caData, RENAME_FAILED);
			}
			write((char*)resPdu, resPdu->uiPDULen);
			free(resPdu);
			resPdu = NULL;
			break;
		}
		case ENUM_MESSAGE_TYPE_ENTER_DIR_REQUEST: {
			QString strEnterDir = QString("%1/%2").arg((char*)(pdu->caMsg))
				.arg((char*)(pdu->caData));
			QFileInfo fileInfo(strEnterDir);
			PDU* resPdu = NULL;
			if (fileInfo.isDir()) {
				QDir dir(strEnterDir);
				QFileInfoList fileInfoList = dir.entryInfoList();
				//去除.与..
				int iFileCount = fileInfoList.size();
				resPdu = mkPDU(sizeof(FileInfo) * (iFileCount - 2));
				resPdu->uiMsgType = ENUM_MESSAGE_TYPE_FLUSH_FILE_RESPOND;
				FileInfo* pFileInfo;
				QString strFileName;
				QByteArray temp;
				for (int i = 2; i < fileInfoList.size(); i++) {
					pFileInfo = (FileInfo*)(resPdu->caMsg) + i - 2;
					strFileName = fileInfoList[i].fileName();
					temp = strFileName.toLocal8Bit();
					memcpy(pFileInfo->caFileName,
						temp.data(), temp.size());
					if (fileInfoList[i].isDir()) {
						pFileInfo->iFileType = 0;
					}
					else if (fileInfoList[i].isFile()) {
						pFileInfo->iFileType = 1;
					}

					qDebug() << fileInfoList[i].fileName()
						<< fileInfoList[i].size();
				}
				write((char*)resPdu, resPdu->uiPDULen);
			}
			else if (fileInfo.isFile()) {	//进入失败，所选为文件
				resPdu = mkPDU(0);
				resPdu->uiMsgType = ENUM_MESSAGE_TYPE_ENTER_DIR_RESPOND;
				strcpy(resPdu->caData, ENTER_DIR_FAILED);
				write((char*)resPdu, resPdu->uiPDULen);
			}
			if (resPdu) {
				free(resPdu);
			}
			resPdu = NULL;
			break;
		}
		case ENUM_MESSAGE_TYPE_UPLOAD_FILE_REQUEST: {
			char caFileName[32] = { '\0' };
			qint64 fileSize = 0;
			//取出文件名字和文件大小
			sscanf(pdu->caData, "%s %lld", caFileName, &fileSize);
			char* pPath = new char[pdu->uiMsgLen];
			memcpy(pPath, pdu->caMsg, pdu->uiMsgLen);
			QString strPath = QString("%1/%2").arg(pPath).arg(caFileName);
			qDebug() << strPath;
			delete[]pPath;
			pPath = NULL;

			//接收文件
			m_file.setFileName(strPath);
			//以只写的方式打开文件，不存在则自动创建文件
			//*************可以改为多线程*************
			if (m_file.open(QIODevice::WriteOnly)) {
				m_bUpload = true;
				m_iTotal = fileSize;
				m_iReceived = 0;
			}
			break;
		}
		case ENUM_MESSAGE_TYPE_DELETE_FILE_REQUEST: {
			QString strDelPath = QString("%1/%2").arg((char*)(pdu->caMsg))
				.arg((char*)(pdu->caData));
			qDebug() << "当前删除的路径：" << strDelPath;
			QFileInfo fileInfo(strDelPath);
			bool ret = false;
			if (fileInfo.isFile()) {
				QFile file;
				file.setFileName(strDelPath);
				ret = file.remove();
			}
			else if (fileInfo.isDir()) {
				ret = false;
			}
			PDU* resPdu = mkPDU(0);
			resPdu->uiMsgType = ENUM_MESSAGE_TYPE_DELETE_FILE_RESPOND;
			if (ret) {
				//删除成功
				strcpy(resPdu->caData, DEL_FILE_OK);
			}
			else {
				strcpy(resPdu->caData, DEL_FILE_FAILED);
			}
			write((char*)resPdu, resPdu->uiPDULen);
			free(resPdu);
			resPdu = NULL;
			break;
		}
		case ENUM_MESSAGE_TYPE_DOWNLOAD_FILE_REQUEST: {
			QString strDownloadFile = QString("%1/%2")
				.arg((char*)(pdu->caMsg)).arg((char*)(pdu->caData));
			QFileInfo fileInfo(strDownloadFile);
			if (fileInfo.exists()) {
				QByteArray baFileName = fileInfo.completeBaseName().toLocal8Bit();
				qint64 fileSize = fileInfo.size();
				PDU* resPdu = mkPDU(0);
				resPdu->uiMsgType = ENUM_MESSAGE_TYPE_DOWNLOAD_FILE_RESPOND;
				sprintf(resPdu->caData, "%s %lld", baFileName.data(), fileSize);
				write((char*)resPdu, resPdu->uiPDULen);
				free(resPdu);
				resPdu = NULL;
				m_file.setFileName(strDownloadFile);
				m_file.open(QIODevice::ReadOnly);
				qDebug() << m_file.fileName() << " 已打开";
				//m_pTimer->start(1000);
			}
			break;
		}
		case ENUM_MESSAGE_TYPE_DOWNLOAD_FILE_DATA_REQUEST: {
			sendFileToClient();
			break;
		}
		case ENUM_MESSAGE_TYPE_ALL_ONLINE_REQUEST: {
			QStringList ret = OpeDB::getInstance().handleAllOnline();
			PDU* resPdu = mkPDU(ret.size() * 32);
			resPdu->uiMsgType = ENUM_MESSAGE_TYPE_ALL_ONLINE_RESPOND;
			for (int i = 0; i < ret.size(); i++) {
				strcpy((char*)resPdu->caMsg + 32 * i, ret[i].toLocal8Bit().data());
			}
			qDebug() << "**********************************************";
			for (int i = 0; i < ret.size(); i++) {
				qDebug() << (char*)resPdu->caMsg + 32 * i;
			}
			qDebug() << "**********************************************";
			write((char*)resPdu, resPdu->uiPDULen);
			free(resPdu);
			resPdu = NULL;
			break;
		}
		case ENUM_MESSAGE_TYPE_SEARCH_USER_REQUEST: {
			int ret = OpeDB::getInstance().handleSearchUsr(pdu->caData);
			PDU* resPdu = mkPDU(0);
			resPdu->uiMsgType = ENUM_MESSAGE_TYPE_SEARCH_USER_RESPOND;
			switch (ret) {
			case -1:
				strcpy(resPdu->caData, USER_NOT_EXIST);
				break;
			case 0:
				strcpy(resPdu->caData, USER_NOT_ONLINE);
				break;
			case 1:
				strcpy(resPdu->caData, USER_IS_ONLINE);
				break;
			}
			write((char*)resPdu, resPdu->uiPDULen);
			free(resPdu);
			resPdu = NULL;
		}
		case ENUM_MESSAGE_TYPE_ADD_FRIEND_REQUEST: {
			/**
			* 1、检索数据库，检查是否已经是好友关系
			* 2、查看在线状态（包含存在状态）
			* 3、向被请求者发送请求
			*/
			char name[32] = { '\0' };
			char perName[32] = { '\0' };
			strcpy(name, pdu->caData);
			strcpy(perName, pdu->caData + 32);
			int ret = OpeDB::getInstance().handleAddFriend(perName, name);
			PDU* resPdu = mkPDU(0);
			resPdu->uiMsgType = ENUM_MESSAGE_TYPE_ADD_FRIEND_RESPOND;
			switch (ret) {
			case -2:
				strcpy(resPdu->caData, UNKNOW_ERROR);
				break;
			case -1:
				strcpy(resPdu->caData, USER_NOT_EXIST);
				break;
			case 0:
				strcpy(resPdu->caData, USER_NOT_ONLINE);
				break;
			case 1:	//未加好友且在线，获取对方socket
				MyTcpServer::getInstance().reSend(perName, pdu);
				strcpy(resPdu->caData, FRIEND_REQUEST_SEND);
				break;
			case 2:
				strcpy(resPdu->caData, FRIEND_ALREADY_EXIST);
				break;
			default:
				break;
			}
			write((char*)resPdu, resPdu->uiPDULen);
			free(resPdu);
			resPdu = NULL;
			break;
		}
		case ENUM_MESSAGE_TYPE_ADD_FRIEND_RESPOND: {
			int flag = pdu->caMsg[0];
			if (flag == -1) {	//未做出反应，不返回信息
				break;
			}
			else {
				PDU* resPdu = mkPDU(0);
				resPdu->uiMsgType = ENUM_MESSAGE_TYPE_ADD_FRIEND_RESPOND;
				char name[32] = { '\0' };
				char perName[32] = { '\0' };
				strcpy(name, pdu->caData);
				strcpy(perName, pdu->caData + 32);
				if (flag) {	//同意添加好友，修改数据库并返回信息给客户端
					int ret = OpeDB::getInstance().addFriend(perName, name);
					if (ret == 1) {
						strcpy(resPdu->caData, ADD_FRIEND_ACCEPTED);
					}
					else {
						strcpy(resPdu->caData, UNKNOW_ERROR);
					}
					write((char*)resPdu, resPdu->uiPDULen);
				}
				else {	//拒绝添加好友，返回信息给对方客户端，不再给自己客户端返回信息
					strcpy(resPdu->caData, ADD_FRIEND_REFUSED);
				}
				MyTcpServer::getInstance().reSend(name, resPdu);
				free(resPdu);
				resPdu = NULL;
				break;
			}
		}
		default:
			break;
		}
		free(pdu);
		pdu = NULL;
	}//end else
	return;
}//end recvMsg()